package com.xncoding.freemarker.ctr;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.xncoding.freemarker.utile.SFtpUtil;
import com.xncoding.freemarker.utile.SftpVo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.*;
import java.util.*;
import org.apache.commons.codec.binary.Base64;

/**
 * @ClassName: SftpController
 * @Description: 上传sftp通用
 * @author: huangyao
 * @date: 2020/5/20 10:19
 */
@RequestMapping("/download/")
@RestController
public class DowloadSftpController {
    protected Log log = LogFactory.getLog(this.getClass());

    @RequestMapping("/singleSftp")
    public Map downloadWithSftp(@RequestBody Map<String, String> params) {
        log.info("singleSftp下载参数："+JSON.toJSONString(params));
        Map<String, Object> resultMap = new HashMap<>();
        String filePath = null;
        //文件名
        String fileName = params.get("fileName");
        //sftp-ip
        String sftpIp = params.get("sftpIp");
        //sftp-端口
        int sftpPort = Integer.parseInt(params.get("sftpPort"));
        //用户名
        String sftpUserName = params.get("sftpUserName");
        //密码
        String sftpPassword = params.get("sftpPassword");
        //sftp根路径
        String sftpPath = params.get("sftpPath");
        //本地存放路径
        String serverPath = params.get("serverPath");
        //登录私钥存放路径,包含文件名
        String privateKeyPath = params.get("privateKeyPath");
        String seriaNo = params.get("seriaNo");
        try {
            //从度小满sftp下载文件
            SftpVo sftpVo = new SftpVo();
            sftpVo.setIp(sftpIp);
            sftpVo.setPort(sftpPort);
            sftpVo.setUser(sftpUserName);
            sftpVo.setPassword(sftpPassword);
            sftpVo.setDownloadDirName(serverPath);
            sftpVo.setSftpDir(sftpPath);
            sftpVo.setFileName(fileName);
            sftpVo.setPrivateKeyPath(privateKeyPath);
            //1  为绝对路径   0为相对路径。
            sftpVo.setFlagPrivateKeyPath("1");
            try {
                if (!SFtpUtil.isFileExist(sftpVo)) {
                    log.info(seriaNo+"文件下载失败，文件不存在{}" + fileName);
                    resultMap.put("result", "fail");
                    resultMap.put("message", "文件下载失败，文件不存在");
                    return resultMap;
                }
                //下载文件
                SFtpUtil.sftpDownload(sftpVo);
                filePath = sftpVo.getDownloadDirName() + fileName;
                log.info(seriaNo+"下载文件成功,保存路径" + filePath);
                byte[] dataByte =  readFile(filePath,seriaNo);
                if(null==dataByte){
                    resultMap.put("result", "fail");
                    resultMap.put("message", "文件为空或被覆盖");
                }else{
                    String dataStr = Base64.encodeBase64String(dataByte);
                    resultMap.put("data", dataStr);
                    resultMap.put("result", "success");
                    resultMap.put("message", "交易成功");
                }

            } catch (Exception e) {
                resultMap.put("result", "fail");
                resultMap.put("message", "文件下载失败:"+e.getMessage());
                log.error(seriaNo+"文件下载失败:"+e.getMessage(),e);
            }
        } catch (Exception e) {
            e.printStackTrace();
            resultMap.put("result", "fail");
            resultMap.put("message", "系统异常:"+e.getMessage());
            log.error(seriaNo+"系统异常："+e.getMessage(),e);
        } finally {
            File file = new File(serverPath + fileName);
            if (file.exists()) {
                boolean delete = file.delete();
                log.info(seriaNo+"读取文件后删除文件:" + serverPath + fileName + "," + (delete ? "成功" : "失败"));
            }
        }
        log.info(seriaNo+"返回状态码:"+resultMap.get("result")+"返回结果:"+ resultMap.get("message"));
        return resultMap;
    }

    /**
     * 批量下载文件
     * 下载整个目录下的文件
     * 但目录下如果子目录，会报错
     * 本机测试总文件大小建议控制在600m以内
     * 如果提升文件总量，可调高jvm堆内存
     * @param params
     * @return
     */
    @RequestMapping("/batchDownloadForFile")
    public Map batchDownloadWithSftp(@RequestBody Map<String, String> params) {
        log.info("batchDownloadForFile下载目录参数："+JSON.toJSONString(params));
        Map resultMap = new HashMap();

        String filePath = null;
        //文件名
        String fileName = null;
        //sftp-ip
        String sftpIp = params.get("sftpIp");
        //sftp-端口
        int sftpPort = Integer.parseInt(params.get("sftpPort"));
        //用户名
        String sftpUserName = params.get("sftpUserName");
        //密码
        String sftpPassword = params.get("sftpPassword");
        //sftp根路径
        String sftpPath = params.get("sftpPath");
        //本地存放路径
        String serverPath = params.get("serverPath");
        //登录私钥存放路径,包含文件名
        String privateKeyPath = params.get("privateKeyPath");
        String seriaNo = params.get("seriaNo");
        try {
            //从度小满sftp下载文件
            SftpVo sftpVo = new SftpVo();
            sftpVo.setIp(sftpIp);
            sftpVo.setPort(sftpPort);
            sftpVo.setUser(sftpUserName);
            sftpVo.setPassword(sftpPassword);
            sftpVo.setDownloadDirName(serverPath);
            sftpVo.setSftpDir(sftpPath);
//            sftpVo.setFileName(fileName);
            sftpVo.setPrivateKeyPath(privateKeyPath);
            //1  为绝对路径   0为相对路径。
            sftpVo.setFlagPrivateKeyPath("1");

            List<String> fileList = null;
            try {
                Map<String, String> singFileMap = null;
                List<Map> resList = null;
                fileList = SFtpUtil.getFileList(sftpVo,"UTF-8");
                if (null == fileList || fileList.size() < 1){
                    log.info(seriaNo+"文件目录为空" + sftpPath);
                    resultMap.put("result", "fail");
                    resultMap.put("message", "文件目录为空");
                }else {
                    //下载文件
                    resList = new ArrayList<>();
                    for (String fileStr:fileList){
                        sftpVo.setFileName(fileStr);
                        singFileMap = DownloadSftpMap(sftpVo, seriaNo);
                        resList.add(singFileMap);
                    }
                    resultMap.put("result", "success");
                    resultMap.put("message", "交易成功");
                    resultMap.put("data", JSON.toJSONString(resList));
                }

            } catch (Exception e) {
                resultMap.put("result", "fail");
                resultMap.put("message", "下载过程失败:"+e.getMessage());
                log.error(seriaNo+"下载过程异常"+e.getMessage(),e);
            }
        } catch (Exception e) {
            resultMap.put("result", "fail");
            resultMap.put("message", "系统异常:"+e.getMessage());
            log.error(seriaNo+"系统异常"+e.getMessage(),e);
        } finally {
//            log.info("文件夹读取最终结果为:"+ JSON.toJSONString(resultMap));
            log.info(seriaNo+"文件夹下载结束");
        }
        return resultMap;
    }

    /**
     * 获取文件夹下文件列表
     * @param params
     * @return
     */
    @RequestMapping("/getFileList")
    public Map getFileList(@RequestBody Map<String, String> params) {
        log.info("getFileList获取文件列表参数："+JSON.toJSONString(params));

        Map resultMap = new HashMap();

        String filePath = null;
        //文件名
        String fileName = null;
        //sftp-ip
        String sftpIp = params.get("sftpIp");
        //sftp-端口
        int sftpPort = Integer.parseInt(params.get("sftpPort"));
        //用户名
        String sftpUserName = params.get("sftpUserName");
        //密码
        String sftpPassword = params.get("sftpPassword");
        //sftp根路径
        String sftpPath = params.get("sftpPath");
        //本地存放路径
        String serverPath = params.get("serverPath");
        //登录私钥存放路径,包含文件名
        String privateKeyPath = params.get("privateKeyPath");
        String seriaNo = params.get("seriaNo");
        try {
            //从度小满sftp下载文件
            SftpVo sftpVo = new SftpVo();
            sftpVo.setIp(sftpIp);
            sftpVo.setPort(sftpPort);
            sftpVo.setUser(sftpUserName);
            sftpVo.setPassword(sftpPassword);
            sftpVo.setDownloadDirName(serverPath);
            sftpVo.setSftpDir(sftpPath);
//            sftpVo.setFileName(fileName);
            sftpVo.setPrivateKeyPath(privateKeyPath);
            //1  为绝对路径   0为相对路径。
            sftpVo.setFlagPrivateKeyPath("1");

            List<String> fileList = null;
            try {
                Map<String, String> singFileMap = null;
                fileList = SFtpUtil.getFileList(sftpVo,"UTF-8");
                if (null == fileList || fileList.size() < 1){
                    log.info(seriaNo+"文件目录为空" + sftpPath);
                    resultMap.put("result", "fail");
                    resultMap.put("message", "文件目录为空");
                }else {
                    resultMap.put("result", "success");
                    resultMap.put("message", "交易成功");
                    resultMap.put("data", JSON.toJSONString(fileList));
                }

            } catch (Exception e) {
                resultMap.put("result", "fail");
                resultMap.put("message", "文件列表获取失败:"+e.getMessage());
                log.error(seriaNo+"文件列表获取失败"+e.getMessage(),e);
            }
        } catch (Exception e) {
            resultMap.put("result", "fail");
            resultMap.put("message", "文件列表获取失败-系统异常"+e.getMessage());
            log.error(seriaNo+"文件列表获取失败-系统异常"+e.getMessage(),e);
        } finally {
//            log.info("文件夹读取最终结果为:"+ JSON.toJSONString(resultMap));
            log.info(seriaNo+"文件列表获取结束");
        }
        log.info(seriaNo+"getFileList返回参数："+JSON.toJSONString(resultMap));
        return resultMap;
    }

    /**
     * 批量下载文件
     * 客户端先调用目录列表然后分批下载
     * 避免目录文件过多，文件量太大
     * 本机测试总文件大小建议控制在600m以内
     * 如果提升文件总量，可调高jvm堆内存
     * @param params
     * @return
     */
    @RequestMapping("/batchDownloadForList")
    public Map batchDownloadForList(@RequestBody Map<String, String> params) {
        log.info("batchDownloadForList批量下载文件参数："+JSON.toJSONString(params));
        Map resultMap = new HashMap();

        String filePath = null;
        //文件名
        String fileName = null;
        //sftp-ip
        String sftpIp = params.get("sftpIp");
        //sftp-端口
        int sftpPort = Integer.parseInt(params.get("sftpPort"));
        //用户名
        String sftpUserName = params.get("sftpUserName");
        //密码
        String sftpPassword = params.get("sftpPassword");
        //sftp根路径
        String sftpPath = params.get("sftpPath");
        //本地存放路径
        String serverPath = params.get("serverPath");
        //登录私钥存放路径,包含文件名
        String privateKeyPath = params.get("privateKeyPath");
        //登录私钥存放路径,包含文件名
        String fileNmaeList = params.get("fileName");
        String seriaNo = params.get("seriaNo");
        try {
            //从度小满sftp下载文件
            SftpVo sftpVo = new SftpVo();
            sftpVo.setIp(sftpIp);
            sftpVo.setPort(sftpPort);
            sftpVo.setUser(sftpUserName);
            sftpVo.setPassword(sftpPassword);
            sftpVo.setDownloadDirName(serverPath);
            sftpVo.setSftpDir(sftpPath);
//            sftpVo.setFileName(fileName);
            sftpVo.setPrivateKeyPath(privateKeyPath);
            //1  为绝对路径   0为相对路径。
            sftpVo.setFlagPrivateKeyPath("1");

            try {
                JSONArray jsonObject = JSON.parseArray(fileNmaeList);

                Map<String, String> singFileMap = null;
                List<Map> resList = null;
                if (null == jsonObject || jsonObject.size() < 1){
                    log.info(seriaNo+"传入文件目录为空");
                    resultMap.put("result", "fail");
                    resultMap.put("message", "传入文件目录为空");
                }else {
                    //下载文件
                    resList = new ArrayList<>();
                    for (Object fileStr:jsonObject){
                        sftpVo.setFileName((String) fileStr);
                        singFileMap = DownloadSftpMap(sftpVo,seriaNo);
                        resList.add(singFileMap);
                    }
                    resultMap.put("result", "success");
                    resultMap.put("message", "交易成功");
                    resultMap.put("data", JSON.toJSONString(resList));
                }

            } catch (Exception e) {
                resultMap.put("result", "fail");
                resultMap.put("message", "下载过程异常"+e.getMessage());
                log.error(seriaNo+"下载过程异常"+e.getMessage(),e);
            }
        } catch (Exception e) {
            resultMap.put("result", "fail");
            resultMap.put("message", "下载失败-系统异常"+e.getMessage());
            log.error(seriaNo+"下载失败-系统异常"+e.getMessage(),e);
        } finally {
//            log.info("文件夹读取最终结果为:"+ JSON.toJSONString(resultMap));
            log.info(seriaNo+"文件夹下载结束");
        }
        return resultMap;
    }

    /**
     * 下载文件
     * @param sftpVo
     * @return
     */
    public Map DownloadSftpMap(SftpVo sftpVo,String seriaNo) {
        Map<String, Object> resultMap = new HashMap<>();
        String filePath = null;
        try {
            if (!SFtpUtil.isFileExist(sftpVo)) {
                log.info(seriaNo+"batch文件下载失败，文件不存在{}" + sftpVo.getFileName());
                resultMap.put("fileName", sftpVo.getFileName());
                resultMap.put("result", "fail");
                resultMap.put("message", "文件不存在");
                return resultMap;
            }
            //下载文件
            SFtpUtil.sftpDownload(sftpVo);
            filePath = sftpVo.getDownloadDirName() + sftpVo.getFileName();
            log.info(seriaNo+"batch下载文件成功,保存路径" + filePath);
            byte[] dataByte =  readFile(filePath,seriaNo);
            String dataStr = Base64.encodeBase64String(dataByte);

            resultMap.put("data", dataStr);
            resultMap.put("fileName", sftpVo.getFileName());
            resultMap.put("result", "success");
            resultMap.put("message", "交易成功");
        } catch (Exception e) {
            resultMap.put("result", "fail");
            resultMap.put("message", "下载失败"+e.getMessage());
            resultMap.put("fileName", sftpVo.getFileName());
            log.error(seriaNo+"下载失败"+e.getMessage(),e);
        }finally {
            File file = new File(sftpVo.getDownloadDirName() + sftpVo.getFileName());
            if (file.exists()) {
                boolean delete = file.delete();
                log.info(seriaNo+"batch读取文件后删除文件:" + sftpVo.getDownloadDirName() + sftpVo.getFileName() + "," + (delete ? "成功" : "失败"));
            }
        }
        log.info(seriaNo+"batch读取文件返回状态码:"+resultMap.get("result")+"返回结果:"+ resultMap.get("message"));
        return resultMap;
    }

    private byte[] readFile(String fromPath,String seriaNo) {
        File fromfile = new File(fromPath);
        FileInputStream inputStream = null;
        byte[]  bytes = null;
        try {
            inputStream = new FileInputStream(fromfile);
            if (null != inputStream ) {
                bytes =   toByteArray(inputStream, seriaNo);
            }
        } catch (Exception e) {
            log.error(seriaNo+"读取文件失败："+e.getMessage(),e);
        }finally {
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error(seriaNo+"关闭输入流失败："+e.getMessage(),e);
                }
            }
            log.info(seriaNo+"文件读取完毕");
            if (null == bytes || bytes.length < 1){
                log.info(seriaNo+"文件读取大小为空");
            }else{
                log.info(seriaNo+"文件读取大小为"+bytes.length);
            }

        }
        return  bytes;
    }

    /**
     * InputStream转化为byte[]数组
     * @param input
     * @return
     * @throws IOException
     */
    public byte[] toByteArray(InputStream input,String seriaNo) {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] resByte = null;
        try {
            byte[] buffer = new byte[4096];
            int n = 0;
            BufferedInputStream bufferedInputStream = new BufferedInputStream(input);
            while (-1 != (n = bufferedInputStream.read(buffer))) {
                output.write(buffer, 0, n);
            }
            resByte =output.toByteArray();
        }catch (Exception e){
            log.error(seriaNo+"文件流读取异常："+e.getMessage(),e);
        }finally {
            if (null != output) {
                try {
                    output.close();
                } catch (IOException e) {
                    log.error(seriaNo+"文件流读取异常："+e.getMessage(),e);
                }
            }

        }
        return resByte;
    }
}
